package mobile.game.core.gen.serializer;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import mobile.game.core.gen.GenBase;
import mobile.game.core.interfaces.ISerilizable;
import mobile.game.core.support.ClassFinder;

/**
 *
 *
 * @author rocks
 * 
 */
public class GenIOSerializer extends GenBase {
	/**  文件后缀 */
	public final String CLASS_SUFFIX = "IOSerializer";
	/**  模板名 */
	public final String TEMP_NAME = "IOSerializer.ftl";
	
	public GenIOSerializer(String packageName, String targetDir) throws Exception {
		super(packageName, targetDir);
		this.init();
	}
	
	/**
	 * 初始化class信息，是否能生成等字段
	 * @throws Exception
	 */
	private void init() throws Exception {
		/**  默认是true */
		this.canGen = true;
		
		// 遍历所有类信息, 获取模板信息, 判断是否出错, 出错则初始化异常不能生成
		Map<Class<?>, List<Field>> classes = getClassInfoToGen();
		for(Entry<Class<?>, List<Field>> entry : classes.entrySet()) {
			Class<?> clazz = entry.getKey();
			List<Field> ms = entry.getValue();
			try {
				this.rootMaps.add(getRootMap(clazz, ms));
			} catch (Exception e) {
				// 如果获取模板内容异常，表示不能生成
				/**  不能生成 */
				this.canGen = false;
				System.err.println("文件存在错误，不继续进行Proxy生成了，错误如下：");
				e.printStackTrace();
			}
		}
		
	}
	
	/**
	 * 生成Proxy类的核心方法
	 * @param rootMap
	 * @throws Exception
	 */
	@Override
	protected void genFileHandler(Map<String, Object> rootMap) throws Exception {
		// 准备参数生成文件
		String fileName = (String) rootMap.get("proxyName");
		String packName = (String) rootMap.get("packageName");
		String targetFileDir = this.targetDir + ClassFinder.packageToPath(packName) + "/";
		
		writeFile(targetFileDir, fileName + ".java", TEMP_NAME, rootMap);
	}
	
	/**
	 * 获取要生成的class及其field
	 * @return
	 * @throws Exception
	 */
	private Map<Class<?>, List<Field>> getClassInfoToGen() throws Exception {
		Map<Class<?>, List<Field>> result = new LinkedHashMap<>();

		// 获取源文件夹下的所有类
		List<Class<?>> sources = ClassFinder.getAllClass(packageName);
		
		// 遍历所有类，取出类中有@DistrClass注解的方法
		for(Class<?> clazz : sources) {
			// 如果没有@DistriClass注解, 则不处理
			if(!clazz.isAnnotationPresent(DistrSerializer.class)) {
				continue;
			}
			
			if(!ISerilizable.class.isAssignableFrom(clazz)){
				continue;
			}

			Field[] fieldArray = clazz.getDeclaredFields();
			List<Field> fields = new ArrayList<>();
			
			// 遍历所有方法,过滤掉静态成员和内部类成员
			for(Field f : fieldArray) {		
				if(!f.isAnnotationPresent(SerializerField.class)) {
					continue;
				}
				
				if(f.getName().contains("$")) {
					continue;
				}
				
				fields.add(f);
			}
			
			result.put(clazz, fields);
		}
		
		return result;
	}
	
	/**
	 * 根据Class及其methods获取填充模板内容
	 * @param clazz
	 * @param fields
	 * @return
	 * @throws Exception 
	 */
	private Map<String, Object> getRootMap(Class<?> clazz, List<Field> fields) throws Exception {
		// 获取实体类名,表名,包名
		String packageName = clazz.getPackage().getName();
		String className = clazz.getSimpleName();
		String proxyName = className + CLASS_SUFFIX;
		
		// 填充Map
		Map<String, Object> rootMap = new HashMap<>();
		List<Map<String, Object>> fieldInfos = new ArrayList<>();
		
		rootMap.put("packageName", packageName);
		rootMap.put("className", className);
		rootMap.put("proxyName", proxyName);
		rootMap.put("fields", fieldInfos);
		
		// 遍历methods，获取方法名，方法参数等信息
		for (Field f : fields) {
			// 模板所需数据
			Map<String, Object> field = new LinkedHashMap<>();
			//首字母大写
			String name = f.getName();
			String chs = name.substring(0,1);
			chs = chs.toUpperCase();
			name = chs + name.substring(1);
			field.put("name", name);
			field.put("type", f.getType().getName());

			fieldInfos.add(field);
		}

		return rootMap;
	}
	
	public static void main(String[] args) throws Exception {
		if(args.length < 2) {
			System.out.println("usage: packageName, targetDir");
			return;
		}
		
		//设置log4j2配置文件所需的环境变量，作用是gen的时候
		//不会报配置没找到的错误，同时有gen.log的日志记录
		System.setProperty("logFileName", "GenIOSerializer");
		
		String packageName = args[0];
		String targetDir = System.getProperty("user.dir") + args[1];
		
		GenBase genIOSerializer = new GenIOSerializer(packageName, targetDir);
		
		// 判断不能生成IOSerializer
		if(!genIOSerializer.isCanGen()) {
			System.err.println("不能生成IOSerializer，请检查重试。。");
			return;
		}
		
		// 测试生成实体类
		genIOSerializer.genFiles();
	}
	
}
